/*
 * Copyright (c) 2020-2099 the original author or authors. All rights reserve.
 */
package org.tianyun.cloud.config.console.sources;

import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.util.CollectionUtils;
import org.tianyun.cloud.utils.RegExUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 配置名称带通配符的PropertySource，通配符支持"*"和"**"，如：
 * <pre>
 * # 全局切换使用单机模式
 * redis.*.host=localhost:7014
 * # 清空哨兵模式配置
 * redis.*.sentinel.**=
 * # 清空集群模式配置
 * redis.*.cluster.**=
 *
 * # 也可以直接忽略前缀，注意要和ConsolePropertySource结合使用
 * host=localhost:7014
 * sentinel.**=
 * cluster.**=
 * </pre>
 *
 * @author ebert_chan
 * @date 2024/8/16
 */
public class NameWildcardPropertySource extends EnumerablePropertySource<Properties> {

    private final Map<String, Pattern> wildcardExpressions = new HashMap<>();

    public NameWildcardPropertySource(String name) {
        this(name, new Properties());
    }

    public NameWildcardPropertySource(String name, Properties source) {
        super(name, source);
        parseSource(source);
    }

    public void load(Properties properties) {
        if (CollectionUtils.isEmpty(properties)) {
            return;
        }
        synchronized (getSource()) {
            getSource().putAll(properties);
            parseSource(properties);
        }
    }

    @Override
    public String[] getPropertyNames() {
        return getSource().keySet().stream().toArray(String[]::new);
    }

    @Override
    public boolean containsProperty(String name) {
        boolean contains = super.containsProperty(name);
        if (contains) {
            return true;
        }
        for (String propertyName : getPropertyNames()) {
            if (RegExUtils.containsWildcardCharacter(propertyName)) {
                Pattern pattern = RegExUtils.wildcardCharacterExpression(propertyName, RegExUtils.DOT, true, true);
                if (pattern.matcher(name).find()) {
                    return true;
                }
            }
        }
        return false;
    }

    @Override
    public Object getProperty(String name) {
        Properties properties = getSource();
        if (CollectionUtils.isEmpty(properties)) {
            return null;
        }

        Object value = properties.get(name);
        if (value != null) {
            return value;
        }

        for (Map.Entry<String, Pattern> entry : wildcardExpressions.entrySet()) {
            String key = entry.getKey();
            Pattern expression = entry.getValue();
            Matcher matcher = expression.matcher(name);
            if (!matcher.find()) {
                continue;
            }
            return properties.get(key);
        }

        return null;
    }

    private void parseSource(Properties source) {
        if (CollectionUtils.isEmpty(source)) {
            return;
        }
        Set<Object> keys = source.keySet();
        for (Object key : keys) {
            if (key == null) {
                continue;
            }

            String strKey = key.toString();
            if (!RegExUtils.containsWildcardCharacter(strKey)) {
                continue;
            }

            wildcardExpressions.put(strKey, RegExUtils.wildcardCharacterExpression(strKey, RegExUtils.DOT, true, true));
        }
    }

}
